package ee.logsimulator.parser;

import ee.logsimulator.data.Actor;
import ee.logsimulator.data.Amount;
import ee.logsimulator.data.Event;
import ee.logsimulator.data.GuildCalendar;
import ee.logsimulator.data.Spell;
import ee.logsimulator.parser.data.Boss;
import ee.logsimulator.parser.data.Raid;
import ee.logsimulator.parser.data.ReportData;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.GetMethod;
import org.htmlparser.Node;
import org.htmlparser.Parser;
import org.htmlparser.Remark;
import org.htmlparser.Tag;
import org.htmlparser.Text;
import org.htmlparser.tags.LinkTag;
import org.htmlparser.tags.TableColumn;
import org.htmlparser.tags.TableRow;
import org.htmlparser.tags.TableTag;
import org.htmlparser.util.NodeList;
import org.htmlparser.util.ParserException;

import com.google.gson.Gson;

public class HtmlParser {

  public void parseRaidPage(String url, Raid raid) throws ParserException {
    Parser parser = new Parser(url);
    parser.setEncoding("UTF-8");

    Node node = findNode(parser.parse(null), "li class='menu'").getNextSibling().getFirstChild().getNextSibling().getFirstChild()
        .getFirstChild().getNextSibling();
    if (node != null) {
      addBosses(raid, node);
    }
  }

  private void addBosses(Raid raid, Node menu) {
    for (Node li : menu.getChildren().toNodeArray()) {
      if (li.getChildren().size() == 1) {
        Node a = li.getChildren().elementAt(0);
        String link = ((LinkTag) a).getAttribute("href");
        String name = a.getChildren().elementAt(0).getText();
        name = name.replace("&#39;", "'");
        raid.bosses.add(new Boss(link, name));
      } else {
        addBosses(raid, li.getChildren().elementAt(1));
      }
    }
  }

  public GuildCalendar parseGuildPage(String url) throws ParserException, IOException {
    HttpClient client = new HttpClient();
    GetMethod get = new GetMethod(url);
    client.executeMethod(get);
    InputStream is = get.getResponseBodyAsStream();

    BufferedReader reader = new BufferedReader(new InputStreamReader(is));
    StringBuilder html = new StringBuilder();
    String line = null;
    while ((line = reader.readLine()) != null) {
      html.append(line + "\n");
    }
    is.close();

    Parser parser = Parser.createParser(html.toString(), "UTF-8");
    parser.setEncoding("UTF-8");
    NodeList list = parser.parse(null);

    Node reports = findNode(list, "div class='calendar-header'");
    String reportsIn = "";
    String left = null;
    String right = null;
    for (Node n : reports.getChildren().toNodeArray()) {
      if (n instanceof LinkTag) {
        if (left == null) {
          left = "http://www.worldoflogs.com" + ((LinkTag) n).extractLink();
        } else if (right == null) {
          right = "http://www.worldoflogs.com" + ((LinkTag) n).extractLink();
        }
      }
      if (n.getText().contains("Reports in")) {
        reportsIn = n.getText().trim();
        break;
      }
    }

    Node calendar = findNode(list, "table class='calendar'");
    List<TableTag> tables = new ArrayList<TableTag>();
    fillAllTableNodes(calendar, tables, true);
    List<Raid> raids = new ArrayList<Raid>();
    for (TableTag tableTag : tables) {
      for (Node n : tableTag.getChildrenAsNodeArray()) {
        String onmouseover = null;
        String link = null;
        String date = null;
        for (Node colN : n.getParent().getParent().getParent().getChildren().toNodeArray()) {
          if (colN instanceof Tag && "date".equals(((Tag) colN).getAttribute("class"))) {
            date = colN.getChildren().elementAt(0).getText();
          }
        }
        if (n instanceof TableRow) {
          TableRow row = (TableRow) n;
          onmouseover = row.getAttribute("onmouseover");
          for (Node rowN : row.getChildrenAsNodeArray()) {
            if (rowN instanceof TableColumn) {
              TableColumn col = (TableColumn) rowN;
              for (Node colN : col.getChildrenAsNodeArray()) {
                if (colN instanceof LinkTag) {
                  link = ((LinkTag) colN).getAttribute("href");
                  break;
                }
              }
            }
          }
          raids.add(new Raid(date, link, onmouseover));
        }
      }
    }

    GuildCalendar guildCalendar = new GuildCalendar(reportsIn, raids, left, right);
    parseJsonPart(html, guildCalendar);
    return guildCalendar;
  }

  private void parseJsonPart(StringBuilder html, GuildCalendar guildCalendar) {
    Gson gson = new Gson();
    for (Raid raid : guildCalendar.raids) {
      String s = raid.onmouseover.split(" ")[1];
      String id = s.substring(0, s.length() - 1);
      String search = "reportData[" + id + "] =";
      int indexOf = html.indexOf(search);
      String json = html.substring(indexOf + search.length(), html.indexOf(";", indexOf));
      ReportData report = gson.fromJson(json, ReportData.class);
      raid.report = report;
    }
  }

  private void fillAllTableNodes(Node node, List<TableTag> tables, boolean first) {
    if (!first && node instanceof TableTag) {
      tables.add((TableTag) node);
    } else if (node.getChildren() != null) {
      for (Node n : node.getChildren().toNodeArray()) {
        fillAllTableNodes(n, tables, false);
      }
    }
  }

  @Deprecated
  public List<Event> parse(String html) throws ParseException, ParserException {
    List<Event> events = new ArrayList<Event>();
    SimpleDateFormat format = new SimpleDateFormat("[HH:mm:ss.SSS]");
    Parser parser = Parser.createParser(html, "UTF-8");
    Node logResults = findNode(parser.parse(null), "div id=\"log-results\"");
    int part = 1;
    Event e = null;
    for (Node node : logResults.getFirstChild().getChildren().toNodeArray()) {
      if (node instanceof Text) {
        Text txt = (Text) node;
        if (part == 1) {
          // date
          e = new Event();
          Date date = getDate(format.parse(txt.getText()));
          e.setTimestamp(date);
          part++;
        } else if (part == 3) {
          // space or hits / crits
          if (!txt.getText().equals(" ")) {
            Spell spell = new Spell(txt.getText().trim());
            e.setSpell(spell);
            part++;
          }
        }
      } else if (node instanceof Tag) {
        Tag tag = (Tag) node;
        if (part == 2) {
          // source
          Tag span = (Tag) tag.getChildren().elementAt(0);
          Actor source = getActor(tag.getAttribute("href"), span.getChildren().elementAt(0).getText(), span.getAttribute("class"), true);
          e.setSource(source);
          part++;
        } else if (part == 3) {
          // spell
          Spell spell = new Spell(tag.getChildren().elementAt(0).getChildren().elementAt(0).getText()
              + tag.getChildren().elementAt(1).getText());
          e.setSpell(spell);
          part++;
        } else if (part == 4) {
          // target
          Tag span = (Tag) tag.getChildren().elementAt(0);
          Actor target = getActor(tag.getAttribute("href"), span.getChildren().elementAt(0).getText(), span.getAttribute("class"), false);
          e.setTarget(target);
          part++;
        } else if (part == 5) {
          // amount
          e.setAmount(new Amount(tag.getChildren().elementAt(0).getText()));
          part++;
        } else if (part == 6) {
          // br
          events.add(e);
          part = 1;
        }
      } else if (node instanceof Remark) {
        System.err.println("HEEEELPPPP!!!");
      }
    }
    return events;
  }

  private Date getDate(Date parse) {
    Calendar cal = Calendar.getInstance();
    cal.setTime(parse);

    if (cal.get(Calendar.HOUR_OF_DAY) < 3) {
      cal.add(Calendar.HOUR_OF_DAY, 24);
    }
    return cal.getTime();
  }

  private Actor getActor(String href, String name, String actorClass, boolean player) {
    String[] split = href.split("/");
    String id = split[4];
    Actor actor = new Actor(split[2], id, name, actorClass);
    return actor;
  }

  private Node findNode(NodeList nodeList, String find) throws ParserException {
    Node result = null;
    Node[] array = nodeList.toNodeArray();
    for (Node node : array) {
      String text = node.getText();
      if (text.equals(find)) {
        return node;
      } else if (result == null && node.getChildren() != null) {
        result = findNode(node.getChildren(), find);
      }
    }
    return result;
  }
}
